﻿//
// Author: Ruxo Zheng (http://ruxozheng.spaces.live.com/)
//
// This file is distributed under CPOL 1.0 License (http://www.codeproject.com/info/cpol10.aspx).
//

using System;
using System.Diagnostics;

namespace RZ.Web
{
    sealed class HtmlLegacyParser : IHtmlParser
    {
        HtmlLegacyLexer lexer;

        internal HtmlLegacyParser(HtmlLegacyLexer lexer)
        {
            this.lexer = lexer;
        }

        public HtmlContent GetNextContent()
        {
            if (this.lexer.EOC)
                throw new InvalidOperationException("No more content.");

            HtmlContent content;

            if (GetHtmlCompleteTag(out content) || GetHtmlOpenTag(out content) || GetHtmlCloseTag(out content) || GetNormalText(out content))
                return content;

            Debug.Assert(this.lexer.IsOpenTag(), "There's another problem other than Tag parsing here!");

            // TODO: Do special treatment for invalid tag by calling user routine.
            // Check http://www.sify.com/

            throw new HtmlParserException("Unrecognized HTML grammar!", this.lexer.Cursor, this.lexer.Content);
        }

        Boolean GetHtmlCompleteTag(out HtmlContent content)
        {
            var startText = this.lexer.Cursor;

            String tagName;

            if (this.lexer.GetBeginOpenTag(out tagName))
            {
                var attributes = GetAttributes();

                if (lexer.SkipCompleteEndOpenTag())
                {
                    content = new HtmlContentCompleteTag(this.lexer.GetContent(startText), tagName, attributes);

                    return true;
                }
            }

            this.lexer.Cursor = startText;

            content = null;
            return false;
        }

        Boolean GetHtmlOpenTag(out HtmlContent content)
        {
            var startText = this.lexer.Cursor;

            String tagName;

            if (this.lexer.GetBeginOpenTag(out tagName))
            {
                var attributes = GetAttributes();

                if (this.lexer.SkipEndTag())
                {
                    content = new HtmlContentOpenTag(this.lexer.GetContent(startText), tagName, attributes);
                    return true;
                }
            }

            this.lexer.Cursor = startText;
            content = null;
            return false;
        }

        Boolean GetHtmlCloseTag(out HtmlContent content)
        {
            var startText = this.lexer.Cursor;

            String tagName;
            if (this.lexer.GetBeginCloseTag(out tagName) && this.lexer.SkipEndTag())
            {
                content = new HtmlContentCloseTag(this.lexer.GetContent(startText), tagName);

                return true;
            }
            else
            {
                this.lexer.Cursor = startText;
                content = null;
                return false;
            }
        }

        Boolean GetNormalText(out HtmlContent content)
        {
            String text;
            var gettable = this.lexer.GetNormalText(out text);

            content = gettable? new HtmlContentText(text) : null;

            return gettable;
        }

        HtmlAttributeCollection GetAttributes()
        {
            var attributes = new HtmlAttributeCollection();

            while (true)
            {
                var saveCursor = this.lexer.Cursor;

                this.lexer.SkipWhiteSpaces();

                String attributeName;
                if (!this.lexer.GetTagName(out attributeName))
                {
                    this.lexer.Cursor = saveCursor;
                    break;
                }

                String attributeValue = String.Empty;

                if (this.lexer.SkipEqualSign())
                {
                    this.lexer.SkipWhiteSpaces();
                    attributeValue = this.GetAttributeValue();
                }

                attributes.Add(attributeName, attributeValue);
            }

            return attributes;
        }

        String GetAttributeValue()
        {
            String text;
            if (lexer.GetQuoteText(out text) || lexer.GetSingleCompromiseWord(out text))
                return text;
            else
                return String.Empty;
        }
    }
}
